package org.hisp.dhis.j2mewsclient;

import java.util.Enumeration;
import java.util.Vector;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.ChoiceGroup;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.StringItem;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

import org.hisp.dhis.wsclient.dao.DataValueDAO;
import org.hisp.dhis.wsclient.dao.DatasetDAO;
import org.hisp.dhis.wsclient.dao.OrganisationUnitDAO;
import org.hisp.dhis.wsclient.dao.PeriodDAO;
import org.hisp.dhis.wsclient.dao.mockup.MockupDataValueDAO;
import org.hisp.dhis.wsclient.dao.mockup.MockupDatasetDAO;
import org.hisp.dhis.wsclient.dao.mockup.MockupOrganisationUnitDAO;
import org.hisp.dhis.wsclient.dao.mockup.MockupPeriodDAO;
import org.hisp.dhis.wsclient.model.DataElement;
import org.hisp.dhis.wsclient.model.DataValue;
import org.hisp.dhis.wsclient.model.Dataset;
import org.hisp.dhis.wsclient.model.OrganisationUnit;
import org.hisp.dhis.wsclient.model.Period;
import org.hisp.dhis.wsclient.generated.DataValueService_Stub;
import org.hisp.dhis.wsclient.generated.*;


public class SimpleMIDlet extends MIDlet implements CommandListener {

    private Display display;

    private ChoiceGroup choiceGroup;

    private Form welcomeScreen;
    private List selectOrgUnitScreen;
    private List selectDatasetScreen;
    private List selectPeriodScreen;
    private Form editDatasetScreen;
    private Form saveDoneScreen;

    private Command cmdOk;
    private Command cmdSave;
    private Command cmdBack;
    private Command cmdExit;
    private Command cmdSelect;

    private OrganisationUnitDAO orgUnitDAO;
    private DatasetDAO datasetDAO;
    private PeriodDAO periodDAO;
    private DataValueDAO dataValueDAO;

    private Vector orgUnits;
    private Vector datasets;
    private Vector periods;
    private Vector fields;

    private OrganisationUnit selectedOrgUnit;
    private Dataset selectedDataset;
    private Period selectedPeriod;

    public void startApp() throws MIDletStateChangeException {

        display = Display.getDisplay(this);

        orgUnitDAO = new MockupOrganisationUnitDAO();
        datasetDAO = new MockupDatasetDAO();
        periodDAO = new MockupPeriodDAO();
        dataValueDAO = new MockupDataValueDAO();

        cmdOk = new Command("Ok", Command.OK, 0);
        cmdSave = new Command("Save", Command.OK, 0);
        cmdBack = new Command("Back", Command.BACK, 1);
        cmdExit = new Command("Exit", Command.EXIT, 2);
        cmdSelect = new Command("Select", Command.ITEM, 1);

        welcomeScreen = createWelcomeScreen();
        
        //Test the dataservice. This code will of course be moved later
        DataValueService dvs = new DataValueService_Stub();
        
    	DataElementType det = new DataElementType();
    	det.setId(1);
    	det.setName("Phil's DataElementName");
    	det.setAlternativeName("AltName");
    	det.setShortName("myShortName");
    	det.setCode("my code");
    	det.setDescription("Some Description");
    	det.setActive(true);
    	det.setType("myType");
    	det.setAggregationOperator("agg");
    	
    	OrganisationUnitType ut = new OrganisationUnitType();
    	ut.setId(2);
    	ut.setActive(true);
    	ut.setClosedDate("2008-01-02");
    	ut.setComment("org comment");
    	ut.setName("org name");
    	ut.setOpeningDate("2007-01-03");
    	ut.setOrganisationUnitCode("myUnitcode");
    	ut.setShortName("phil shortName2");
    	
    	PeriodTypeType ptt = new PeriodTypeType();
    	ptt.setId(3);
    	ptt.setName("Period type type name");
    	
    	PeriodType pt = new PeriodType();
    	pt.setId(3);
    	pt.setPeriodType(ptt);
    	pt.setStartDate("2008-01-01");
    	pt.setEndDate("2008-01-10");  
    	
    	DataValueType myDvt = new DataValueType();
    	myDvt.setDataElement(det);
    	myDvt.setSource(ut);
    	myDvt.setPeriod(pt);
    	myDvt.setValue("myValue");
    	myDvt.setComment("myComment");
    	myDvt.setStoredBy("myStoredBy");
    	myDvt.setTimeStamp("myTimeStamp");
        
        try {
        	DataValueType dv = dvs.getDataValue(det, ut, pt);
        	System.out.println("Got data element");
        	if(dv != null)
        	  System.out.println("Datavalue : " + dv.getValue());
        	else
        	  System.out.println("The returned datavalue was null");
        	
        }
        catch (java.rmi.RemoteException ex)
        {
        	System.out.println("Exception when calling getDataValue : " + ex.toString());
        }
        
        try {

        	dvs.addDataValue(myDvt);
        	
        }
        catch (java.rmi.RemoteException ex)
        {
        	System.out.println("Exception when calling addDataValue : " + ex.toString());
        }        

        /*
         * Runtime rt = Runtime.getRuntime(); welcomeForm.append("Free Memory: "
         * + rt.freeMemory()); welcomeForm.append("Total Memory: " +
         * rt.totalMemory());
         */

        // welcomeForm.append(dhisSystem.getOrganisationUnit(1).getName());
        System.out.println("Starting app");

        display.setCurrent(welcomeScreen);
    }

    public void pauseApp() {

    }

    public void destroyApp(boolean unconditional) throws MIDletStateChangeException {
        notifyDestroyed();
    }

    public void commandAction(Command c, Displayable d) {

        System.out.println("Command " + c.getLabel());
        if (c == cmdExit) {
            try {
                destroyApp(true);
            } catch (MIDletStateChangeException e) {
                showException(e);
            }
            return;
        }

        if (display.getCurrent() == welcomeScreen) {
            if (c == cmdOk) {
                String selected = choiceGroup.getString(choiceGroup.getSelectedIndex());
                System.out.println(selected);
                if (selected.toLowerCase().indexOf("data") > -1 && selected.toLowerCase().indexOf("entry") > -1) {
                    selectOrgUnitScreen = createSelectOrgUnitScreen();
                    display.setCurrent(selectOrgUnitScreen);
                } else if (selected.toLowerCase().indexOf("simplified") > -1) {
                    editDatasetScreen = createEditDatasetScreen();
                    display.setCurrent(editDatasetScreen);
                }
            }

        } else if (display.getCurrent() == selectOrgUnitScreen) {
            if (c == cmdSelect) {
                int selected = selectOrgUnitScreen.getSelectedIndex();
                selectedOrgUnit = (OrganisationUnit)orgUnits.elementAt(selected);
                System.out.println(selectedOrgUnit.getName());

                selectDatasetScreen = createSelectDatasetScreen();
                display.setCurrent(selectDatasetScreen);

            } else if (c == cmdBack) {
                display.setCurrent(welcomeScreen);
            }

        } else if (display.getCurrent() == selectDatasetScreen) {
            if (c == cmdSelect) {

                int selected = selectDatasetScreen.getSelectedIndex();
                selectedDataset = (Dataset)datasets.elementAt(selected);
                System.out.println(selectedOrgUnit.getName());

                selectPeriodScreen = createSelectPeriodScreen();
                display.setCurrent(selectPeriodScreen);

            } else if (c == cmdBack) {
                display.setCurrent(selectOrgUnitScreen);
            }

        } else if (display.getCurrent() == selectPeriodScreen) {
            if (c == cmdSelect) {

                int selected = selectPeriodScreen.getSelectedIndex();
                selectedPeriod = (Period)periods.elementAt(selected);
                System.out.println(selectedPeriod.getName());

                editDatasetScreen = createEditDatasetScreen();
                display.setCurrent(editDatasetScreen);

            } else if (c == cmdBack) {
                display.setCurrent(selectDatasetScreen);
            }

        } else if (display.getCurrent() == editDatasetScreen) {
            if (c == cmdSave) {

                for (int i = 0; i < fields.size(); ++i) {
                    Field field = (Field)fields.elementAt(i);

                    DataElement dataElement = field.getDataElement();
                    DataValue dataValue = field.getDataValue();
                    String original = field.getOriginalValue();
                    Item item = field.getItem();

                    // Get value from formitem
                    String value = "";
                    if (item.getClass() == TextField.class) {
                        value = ((TextField)item).getString();
                    } else if (item.getClass() == ChoiceGroup.class) {
                        value = ((ChoiceGroup)item).isSelected(0) ? "1" : "0";
                    }

                    // If a value already exists
                    if (dataValue != null && !field.isNew()) {
                        // If this value has been updated
                        if (!original.equals(value)) {
                            System.out.println("Value for element " + dataElement.getName() + " is modified.");
                            dataValue.setValue(value);

                            field.setUpdated(true);
                            saveField(field);
                        } else {
                            // Remove any annotations from previous succsessful
                            // save
                            field.setStatus(Field.UNCHANGED);
                            System.out.println("Value for element " + dataElement.getName() + " is unchanged.");
                        }
                    } else {
                        // If this is a new value
                        if (!value.equals("")) {
                            System.out.println("Value for element " + dataElement.getName() + " is new.");

                            dataValue = new DataValue();
                            dataValue.setOrgUnit(selectedOrgUnit);
                            dataValue.setPeriod(selectedPeriod);
                            dataValue.setDataElement(dataElement);
                            dataValue.setValue(value);

                            field.setDataValue(dataValue);
                            field.setNew(true);
                            saveField(field);
                        } else {
                            System.out.println("Value for element " + dataElement.getName() + " is unchanged.");
                        }
                    }
                }

                saveDoneScreen = createSaveDoneScreen();
                display.setCurrent(saveDoneScreen);

            } else if (c == cmdBack) {
                display.setCurrent(selectPeriodScreen);
            }

        } else if (display.getCurrent() == saveDoneScreen) {
            if (c == cmdBack) {
                display.setCurrent(editDatasetScreen);
            }

        }
    }

    public void saveField(Field field) {

        /*
         * TODO: Should do the following in a seperate thread to avoid locking
         * up the device.
         */

        if (field.isNew()) {
            // Save new value
            try {
                dataValueDAO.newDataValue(field.getDataValue());

                // If save successful
                field.setNew(false);
                field.setOriginalValue(field.getDataValue().getValue());
                field.setStatus(Field.SAVED);

            } catch (Exception e) {
                // If save failed
                field.setStatus(Field.SAVEFAILED);
            }
        } else if (field.isUpdated()) {
            // Update modified value
            try {
                dataValueDAO.updateDataValue(field.getDataValue());

                // If save successful
                field.setUpdated(false);
                field.setOriginalValue(field.getDataValue().getValue());
                field.setStatus(Field.SAVED);

            } catch (Exception e) {
                // If save failed
                field.setStatus(Field.SAVEFAILED);
            }
        }
    }

    public void showException(Exception e) {
        Alert alert = new Alert("Error");
        alert.setString(e.getMessage());
        alert.setType(AlertType.ERROR);
        alert.setTimeout(Alert.FOREVER);
        display.setCurrent(alert);
    }

    public Form createWelcomeScreen() {
        Form form = new Form("DHIS");
        form.addCommand(cmdExit);
        form.addCommand(cmdOk);
        form.append("Welcome to the DHIS mobile client.\n");
        String[] elements = { "Data entry", "Simplified add/modify data for dataset (Spike)" };
        choiceGroup = new ChoiceGroup("Please select an action from the list below", ChoiceGroup.EXCLUSIVE, elements,
            null);
        form.append(choiceGroup);

        form.setCommandListener(this);

        return form;
    }

    public List createSelectOrgUnitScreen() {

        orgUnits = orgUnitDAO.listOrganisationUnits(null);

        List list = new List("Select organisation unit", List.IMPLICIT);

        for (Enumeration e = orgUnits.elements(); e.hasMoreElements();) {
            list.append(((OrganisationUnit)e.nextElement()).getName(), null);
        }

        list.addCommand(cmdSelect);
        list.addCommand(cmdBack);
        list.setCommandListener(this);

        return list;
    }

    public List createSelectDatasetScreen() {
        datasets = datasetDAO.listDatasets(selectedOrgUnit);
        List list = new List("Available datasets", List.IMPLICIT);
        for (Enumeration e = datasets.elements(); e.hasMoreElements();) {
            list.append(((Dataset)e.nextElement()).getName(), null);
        }

        list.setSelectCommand(cmdSelect);
        list.addCommand(cmdBack);
        list.setCommandListener(this);

        return list;
    }

    public List createSelectPeriodScreen() {
        periods = periodDAO.listPeriods(selectedOrgUnit, selectedDataset);
        List list = new List("Available periods", List.IMPLICIT);

        for (Enumeration e = periods.elements(); e.hasMoreElements();) {
            list.append(((Period)e.nextElement()).getName(), null);
        }

        list.setSelectCommand(cmdSelect);
        list.addCommand(cmdBack);
        list.setCommandListener(this);

        return list;
    }

    public Form createEditDatasetScreen() {
        Form form = new Form("Update dataset");

        fields = new Vector();

        Dataset dataset = datasetDAO.getDataset(selectedOrgUnit, selectedPeriod, selectedDataset);
        Vector elements = dataset.getDataElements();
        Vector values = dataset.getDataValues();

        for (Enumeration e = elements.elements(); e.hasMoreElements();) {
            DataElement dataElement = (DataElement)e.nextElement();
            Field field = new Field();

            field.setDataElement(dataElement);

            // Find datavalue for dataelement if there is one
            DataValue dataValue = null;
            for (Enumeration e2 = values.elements(); e2.hasMoreElements();) {
                dataValue = (DataValue)e2.nextElement();
                if (dataValue.getDataElement() == dataElement) {
                    field.setDataValue(dataValue);
                    field.setOriginalValue(dataValue.getValue());
                    break;
                } else {
                    dataValue = null;
                }

            }

            // Create formitems and set values when there are any
            String value = (dataValue != null) ? dataValue.getValue() : "";
            Item item;
            if (dataElement.getType().equalsIgnoreCase("string")) {
                item = new TextField(dataElement.getName(), value, 100, TextField.ANY);

            } else if (dataElement.getType().equalsIgnoreCase("int")) {
                item = new TextField(dataElement.getName(), value, 100, TextField.NUMERIC);

            } else if (dataElement.getType().equalsIgnoreCase("bool")) {
                ChoiceGroup cg = new ChoiceGroup("", ChoiceGroup.MULTIPLE);
                cg.append(dataElement.getName(), null);
                boolean flags[] = { value.equals("1") };
                cg.setSelectedFlags(flags);
                item = cg;

            } else {
                item = new StringItem(dataElement.getName(), "Unsupported datatype.");
            }

            field.setItem(item);
            form.append(item);

            fields.addElement(field);
        }

        form.addCommand(cmdSave);
        form.addCommand(cmdBack);

        form.setCommandListener(this);

        return form;
    }

    public Form createSaveDoneScreen() {
        Form form = new Form("Save done");

        /*
         * TODO: Indicate progress and results of saving changes since each
         * element is saved seperately.
         */
        form.append("Data \"saved\".");
        form.addCommand(cmdBack);

        form.setCommandListener(this);

        return form;
    }
}
